﻿using System.Data;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;

namespace TwinHead.Frameworks.Data
{
    /// <summary>
    /// 데이터셋을 압축하거나 복원하는 기능을 제공합니다.
    /// </summary>
    public static class DataSetCompresser
    {
        /// <summary>
        /// 데이터셋을 바이트 배열로 압축합니다.
        /// </summary>
        /// <param name="ds">작업대상이 되는 데이터셋 객체</param>
        /// <returns></returns>
        public static byte[] CompressDataSet( DataSet ds )
        {
            //1. 데이터셋 Serialize
            ds.RemotingFormat = SerializationFormat.Binary;
            byte[] inbyt = null;

            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize( ms, ds );
                inbyt = ms.ToArray();
            }

            //2. 데이터 압축
            using (MemoryStream objStream = new MemoryStream())
            {
                DeflateStream objZS = new DeflateStream( objStream, CompressionMode.Compress );
                objZS.Write( inbyt, 0, inbyt.Length );
                objZS.Flush();
                //objZS.Close();

                //3. 데이터 리턴
                return objStream.ToArray();
            }
        }

        /// <summary>
        /// 바이트 배열을 데이터셋으로 복원합니다.
        /// </summary>
        /// <param name="bytDs">작업대상이 되는 바이트 배열</param>
        /// <returns></returns>
        public static DataSet DecompressDataSet( byte[] bytDs )
        {
            DataSet outDs = new DataSet();

            //스트림으로 가져오기
            MemoryStream inMs = new MemoryStream( bytDs );
            inMs.Seek( 0, 0 );

            //1. 압축객체 생성- 압축 풀기
            DeflateStream zipStream = new DeflateStream( inMs, CompressionMode.Decompress, true );
            byte[] outByt = ReadFullStream( zipStream );
            zipStream.Flush();
            zipStream.Close();

            //2. 스트림으로 다시변환
            MemoryStream outMs = new MemoryStream( outByt );
            outMs.Seek( 0, 0 );
            outDs.RemotingFormat = SerializationFormat.Binary;

            //3. 데이터셋으로 Deserialize
            BinaryFormatter bf = new BinaryFormatter();
            outDs = (DataSet)bf.Deserialize( outMs, null );
            return outDs;

        }

        /// <summary>
        /// 전달된 스트림을 모두 읽어 바이트 배열로 반환합니다.
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        internal static byte[] ReadFullStream( Stream stream )
        {
            byte[] buffer = new byte[32768];
            using (MemoryStream ms = new MemoryStream())
            {
                while (true)
                {
                    int read = stream.Read( buffer, 0, buffer.Length );
                    if (read <= 0)
                        return ms.ToArray();
                    ms.Write( buffer, 0, read );
                }
            }
        }
    }
}
